install.packages('ggplot2')

The data

This data set contains 113,937 loans with 81 variables on each loan, including loan amount, borrower rate (or interest rate), current loan status, borrower income, borrower employment status, borrower credit history, and the latest payment information.

//TODO Explorte 10 - 15 Variables

library(ggplot2)
loanData <- read.csv('prosperLoanData.csv')
head(loanData)

First Look

In this data set there are 81 Variables. To Start the most interresting for me seem to be # https://www.prosper.com/Downloads/Services/Documentation/ProsperDataExport_Details.html

What? * LoanOriginalAmount * Term: The length of the loan expressed in months. * Loan Origin Date

Who? * isBorrowerHomeOwner * Occupation * CreditGrade * CurrentDelinquencies

Why? * ListingCategory: numeric ** The category of the listing that the borrower selected when posting their listing: 0 - Not Available, 1 - Debt Consolidation, 2 - Home Improvement, 3 - Business, 4 - Personal Loan, 5 - Student Use, 6 - Auto, 7- Other, 8 - Baby&Adoption, 9 - Boat, 10 - Cosmetic Procedure, 11 - Engagement Ring, 12 - Green Loans, 13 - Household Expenses, 14 - Large Purchases, 15 - Medical/Dental, 16 - Motorcycle, 17 - RV, 18 - Taxes, 19 - Vacation, 20 - Wedding Loans

Help from Friends? * InvestementFromFriendsAmount * Investors

Is your credit rate ok? BorrowerAPR: The Borrower’s Annual Percentage Rate (APR) for the loan. BorrowerRate: The Borrower’s interest rate for this loan. The is the rate the borrower pays if the loan were to close at this point in time. The rate is computed as the LenderRate + GroupLeaderRewardRate (if applicable) + BankDraftFeeAnnualRate (if applicable).

*ProsperScore: A custom risk score built using historical Prosper data. The score ranges from 1-10, with 10 being the best, or lowest risk score. Applicable for loans originated after July 2009.

LoanOriginalAmount and Terms by Year - extract the year of the LoanOriginData and save it to a new variable

How much money is needed?

ggplot(aes(x = LoanOriginalAmount), data = loanData) + 
  geom_histogram( fill = ('#F79420')) 

summary(loanData$LoanOriginalAmount)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1000    4000    6500    8337   12000   35000 

create a new DF with info about the prosper rating and the amount

library(dplyr)
creditsByGrade <- group_by(loanData, ProsperRating..numeric.)
creditsByGrade <- summarise(creditsByGrade,
    mean_amount = mean(LoanOriginalAmount), 
    median_amount = median(LoanOriginalAmount),
    min_amount = min(LoanOriginalAmount),
    max_amount = max(LoanOriginalAmount),
    n = n()) 
creditsByGrade

Most of loans are under 5000,–. I suggest the money is needed for sonderausgaben, die im Haushaltsbudget keinen Platz mehr gefunden haben. Möglicherweise Schäden im Haushalt oder Sonderausgaben aufgrund von unvorhersehbaren Umständen. Unfälle…

Sehen wir uns nun an, fĂ¼r was das Geld benötigt wurde. -> unsere Annahme bestägigen

# transform the numeric value of categories to readable names:
loanData$Category[loanData$ListingCategory..numeric. == '0'] <- 'Not Available'
loanData$Category[loanData$ListingCategory..numeric. == '1'] <- 'Debt Consolidation'
loanData$Category[loanData$ListingCategory..numeric. == '2'] <- 'Home Improvement'
loanData$Category[loanData$ListingCategory..numeric. == '3'] <- 'Business'
loanData$Category[loanData$ListingCategory..numeric. == '4'] <- 'Personal Loan'
loanData$Category[loanData$ListingCategory..numeric. == '5'] <- 'Student Use'
loanData$Category[loanData$ListingCategory..numeric. == '6'] <- 'Auto'
loanData$Category[loanData$ListingCategory..numeric. == '7'] <- 'Other'
loanData$Category[loanData$ListingCategory..numeric. == '8'] <- 'Baby & Adoption'
loanData$Category[loanData$ListingCategory..numeric. == '9'] <- 'Boat'
loanData$Category[loanData$ListingCategory..numeric. == '10'] <- 'Cosmetic Procedure'
loanData$Category[loanData$ListingCategory..numeric. == '11'] <- 'Engagement Ring'
loanData$Category[loanData$ListingCategory..numeric. == '12'] <- 'Green Loans'
loanData$Category[loanData$ListingCategory..numeric. == '13'] <- 'Household Expenses'
loanData$Category[loanData$ListingCategory..numeric. == '14'] <- 'Large Purchases'
loanData$Category[loanData$ListingCategory..numeric. == '15'] <- 'Medical/Dental'
loanData$Category[loanData$ListingCategory..numeric. == '16'] <- 'Motorcycle'
loanData$Category[loanData$ListingCategory..numeric. == '17'] <- 'RV'
loanData$Category[loanData$ListingCategory..numeric. == '18'] <- 'Taxes'
loanData$Category[loanData$ListingCategory..numeric. == '19'] <- 'Vacation'
loanData$Category[loanData$ListingCategory..numeric. == '20'] <- 'Wedding Loans'

WHy?

reorder_size <- function(x) {
  factor(x, levels = names(sort(table(x))))
}
ggplot(data= subset(loanData,
                    LoanOriginalAmount > 2500 & LoanOriginalAmount <= 9000 & Category != 'Debt Consolidation' & Category != 'Not Available' & Category != 'Other'), aes(reorder_size(Category))) +
    geom_bar(colour = "green")+
    ggtitle("Amount of loans by category") +
  theme(axis.text.x=element_text(angle=45,hjust=1,vjust=0.5))+
    labs(x="", y = "Number of loans") +
  coord_flip()

As the most categories are debt consolidation, not availabe and other we cant really tell if it is like suggested. The data is not complete. But mostly money is needed for household expenses or home improvement. WHO?

summary(loanData$Occupation)

To make this easier to read, we want to combine groups into a new data frame. Students, Tradesman, Health


loanData$GroupedOccupation <- factor(loanData$Occupation)
levels(loanData$GroupedOccupation) <- list(
  Student=c("Student - College Graduate Student",
"Student - College Senior", 
"Student - Community College",
"Student - College Freshman",
"Student - College Junior",
"Student - College Sophomore",
"Student - Technical School"), 
Medical_Health=c("Doctor", "Nurse's Aide",
                 "Nurse (RN)",
                 "Nurse (LPN)",
                 "Dentist",
                 "Pharmacist",
                 "Medical Technician",
                 "Psychologist"),
Sales=c("Sales - Commission",
        "Sales - Retail",
        "Car Dealer",
        "Realtor"),
Service=c("Food Service Management",
          "Food Service",
          "Postal Service",
          "Social Worker",
          "Truck Driver",
          "Bus Driver",
          "Retail Management",
          "Waiter/Waitress",
          "Flight Attendant",
          "Clerical",
          "Religious",
          "Clergy"),
Laborer=c("Construction",
          "Laborer",
          "Skilled Labor",
          "Landscaping",
          "Homemaker",
          "Fireman",
          "Executive",
          "Teacher's Aide",
          "Computer Programmer",
          "Administrative Assistant",
          "Professional",
          "Accountant/CPA",
          "Tradesman - Carpenter",
            "Tradesman - Mechanic",
            "Tradesman - Electrician",
            "Tradesman - Plumber",
          "Pilot - Private/Commercial"),
HigherEdJobs=c("Architect",
               "Biologist",
               "Engineer - Electrical",
               "Engineer - Mechanical",
               "Engineer - Chemical",
               "Judge", "Teacher",
               "Scientist",
               "Professor",
               "Attorney", "Analyst", "Accountant/CPA"
               ),
CivilService=c("Civil Service",
               "Military Officer",
               "Police Officer/Correction Officer",
               "Military Enlisted"),
Other=c("Other", "")
)


ggplot(data=subset(loanData, GroupedOccupation != 'Other' & !is.na(GroupedOccupation)), x=GroupedOccupation, aes(reorder_size(GroupedOccupation))) +
    geom_bar()+
    ggtitle("Borrowers by Occupation")+
    labs(x="", y = "Number of loans")+
        theme(axis.text.x=element_text(angle=45,hjust=1,vjust=0.5))

In the next step I want to proof the people who aren t house owners need more often small loans for Vacation, HomeImprovement or Household Expenses.

summary(loanData$IsBorrowerHomeowner)

house owner need less credits NEXt: limit to smaller amount of loan and check if loan taker is a house owner

  qplot(x=IsBorrowerHomeowner, y=LoanOriginalAmount,
        data=loanData, geom = 'boxplot') +
  scale_y_continuous(limits = c(1000, 20000))

relation between prosper rate and houseOwner


qplot(x=IsBorrowerHomeowner, y=ProsperScore,
        data=loanData, geom = 'boxplot') +
  scale_y_continuous()
ggplot(aes(x = IsBorrowerHomeowner, y = LoanOriginalAmount),
      data = loanData) +
      stat_summary(fun.y = mean, geom= 'point', shape = 4)

ggplot(aes(x=GroupedOccupation, y=LoanOriginalAmount),
      data = loanData) +
      theme(axis.text.x=element_text(angle=45,hjust=1,vjust=0.5))+
      geom_point(aes(color= IsBorrowerHomeowner, alpha=1/20))
ggplot(aes(x=GroupedOccupation, y=LoanOriginalAmount),
      data = loanData) +
      theme(axis.text.x=element_text(angle=45,hjust=0.5,vjust=0.5))+
      geom_point(aes(color= IsBorrowerHomeowner))+
scale_y_continuous(limits=c(0, 5000))

for money less than 5000 the majority of the borrowers aren t houseowners. Maybe the grouping is not correct. ie in group mediacl nd help there are doctors, nurses et…

check if homeowner have a better Prosperscore

Prosper A custom risk score built using historical Prosper data. The score ranges from 1-10, with 10 being the best, or lowest risk score.

BorrowerRate: The is the rate the borrower pays if the loan were to close at this point in time. The rate is computed as the LenderRate + GroupLeaderRewardRate (if applicable) + BankDraftFeeAnnualRate (if applicable).

Borrower Rate: The is the rate the borrower pays if the loan were to close at this point in time. The rate is computed as the LenderRate + GroupLeaderRewardRate (if applicable) + BankDraftFeeAnnualRate (if applicable).

ProsperScore: A custom risk score built using historical Prosper data. The score ranges from 1-10, with 10 being the best, or lowest risk score.

Total Credit Lines past 7 years:

summary(loanData$MonthlyLoanPayment)
#prosper score -> house owners have a better one
ggplot(aes(x=CurrentDelinquencies, y=..count../sum(..count..)), data = subset(loanData, !is.na(IsBorrowerHomeowner))) +
  geom_freqpoly(aes(color = IsBorrowerHomeowner)) + 
  xlab('Prosper Rating') + 
  ylab('Percentage of Borrowers with that Prosper Rating')
ggplot(aes(x=BorrowerRate, y=ProsperScore), data = loanData)+
  geom_line()+
  geom_smooth()

no surprises here, the better the Prosper Score, the better the Borrower Rate -> Hockey Stick ??

byIncomeRAnge..

When was the money needed?

loanData$LoanOriginationYear <- format(as.Date(loanData$LoanOriginationDate, format="%Y-%m-%d"),"%Y")

ggplot(aes(x = LoanOriginationYear, y = LoanOriginalAmount, fill=Term, group=LoanOriginationYear), data =loanData) +
    geom_bar(aes(group = LoanOriginationYear), position='dodge', stat='identity')+
    ggtitle("Loans by Year") +
    labs(x="Loan origination year", y = "Loan original amount")

IncomeRange:

Income: The income range of the borrower at the time the lisitng was created is one of the following values: 0 - Not displayed 1 - $0 or unable to verify 2 - $1-24,999 3 - $25,000-49,999 4 - $50,000-74,999 5 - $75,000-99,999 6 - $100,000+ 7 - Not Employed

Income verifyalble:

CurrentDelinquenies: Number of current delinquencies at the time the listing was created.

Is there a connection between income of borrowers and

ggplot(aes(x=ProsperRating..numeric., y=..count../sum(..count..)), data = subset(loanData, !is.na(IncomeVerifiable))) +
  geom_freqpoly(aes(color = IncomeVerifiable)) + 
  xlab('Prosper Rating') + 
  ylab('Percentage of Borrowers with that Prosper Rating')

EmploymentStatus: Employment Status Duration In Months.


summary(loanData$EmploymentStatusDuration)
loanData$bucket_EmploymentStatusDuration <- cut(loanData$EmploymentStatusDuration,
                               c(0, 12, 24, 36, 48, 50, 62, 74, 86, 98, 110, 122, 134, 146, 755))


ggplot(aes(x = CreditGrade, y=EmploymentStatusDuration),
       data = loanData) +
  geom_point(aes(color = 
                  EmploymentStatus))
library(dplyr)

ggplot(aes(x=CreditGrade, y=EmploymentStatusDuration), data = loanData) + 
  geom_point(aes(color=EmploymentStatus))

#ggplot(aes(x=LoanOriginationDate, y=LoanOriginalAmount), data = loanData) + 
#  geom_point(aes(color=IncomeRange))

#  detach("package:plyr", unload=TRUE)
ggplot(aes(x=CreditGrade, y=bucket_EmploymentStatusDuration), data = loanData) + 
  geom_point(aes(color=EmploymentStatus))
ggplot(aes(x=Term, y=LoanOriginalAmount), data = loanData) +
    facet_wrap(~GroupedOccupation) +
    geom_point(aes(color = Category)) +
  scale_y_continuous(limits=c(1000, 5000))
loanData$loan_income_ratio = loanData$LoanOriginalAmount/loanData$StatedMonthlyIncome
ggplot(data = loanData, aes(x = loan_income_ratio)) +                
        geom_histogram(color = "black", fill = '#007EE5', binwidth = 0.02) +
        xlim(0, quantile(loanData$loan_income_ratio, prob = 0.57, na.rm=TRUE)) +
        ggtitle("Debt To Income Ratio") +
        xlab("Debt to Income Ratio") +
        ylab("Count")
summary(loanData$loan_income_ratio)

LoanOriginalAmount: Amount needed

Borrower Rate: The borrower rate for this loan.

Lender Yield: The interest rate the lender receives on the loan.

library(dplyr)
ggplot(loanData, aes(x = LenderYield, y = BorrowerRate)) +
  geom_point(alpha=1/20) +
  scale_x_continuous(limits=c(0, quantile(loanData$LenderYield, 0.99))) +
  scale_y_continuous( 
                     limits=c(0 , quantile(loanData$BorrowerRate, 0.99)))
ggplot(loanData, aes(x = BorrowerRate, y = LenderYield,
                     color = IsBorrowerHomeowner, size=count)) +
  geom_point() +
  geom_smooth() +
  geom_point(data = subset(loanData, Category %in% c(
    
'Debt Consolidation', 'Home Improvement'
,'Business'
,'Personal Loan'
,'Student Use'
,'Auto'
,'Other'
,'Baby & Adoption'
,'Boat'
,'Cosmetic Procedure'
,'Engagement Ring'
,'Green Loans'
,'Household Expenses'
,'Large Purchases'
,'Medical/Dental'
,'Motorcycle'
,'RV'
,'Taxes'
,'Vacation'
,'Wedding Loans'
  )), 
             aes(label = Loans),
             color="#0266c8") +
  geom_text(data = subset(loanData, Category %in% c(
    
    'Debt Consolidation', 'Home Improvement'
,'Business'
,'Personal Loan'
,'Student Use'
,'Auto'
,'Other'
,'Baby & Adoption'
,'Boat'
,'Cosmetic Procedure'
,'Engagement Ring'
,'Green Loans'
,'Household Expenses'
,'Large Purchases'
,'Medical/Dental'
,'Motorcycle'
,'RV'
,'Taxes'
,'Vacation'
,'Wedding Loans')),
            aes(label = Loans),
            color = "black",
            size = 3,
            vjust = -0.5) +
  #geom_abline(intercept = 0, slope = 1, linetype=2, color="orange") +
  ylab("Average Science Score") + 
  xlab("Average Reading Score") + 
  ggtitle("Average Reading and Science Scores per country, by gender")
Ignoring unknown aesthetics: labelError in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE,  : 
  Argumente implizieren unterschiedliche Anzahl Zeilen: 113937, 0

what happenend in 2011?

nExt:

Problems

-Convert Origin Date to only the year.. -Map Readable values to Category numeric.. - Job duration in months

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQppbnN0YWxsLnBhY2thZ2VzKCdnZ3Bsb3QyJykNCmBgYA0KI1RoZSBkYXRhDQoNClRoaXMgZGF0YSBzZXQgY29udGFpbnMgMTEzLDkzNyBsb2FucyB3aXRoIDgxIHZhcmlhYmxlcyBvbiBlYWNoIGxvYW4sIGluY2x1ZGluZyBsb2FuIGFtb3VudCwgYm9ycm93ZXIgcmF0ZSAob3IgaW50ZXJlc3QgcmF0ZSksIGN1cnJlbnQgbG9hbiBzdGF0dXMsIGJvcnJvd2VyIGluY29tZSwgYm9ycm93ZXIgZW1wbG95bWVudCBzdGF0dXMsIGJvcnJvd2VyIGNyZWRpdCBoaXN0b3J5LCBhbmQgdGhlIGxhdGVzdCBwYXltZW50IGluZm9ybWF0aW9uLg0KDQovL1RPRE8gRXhwbG9ydGUgMTAgLSAxNSBWYXJpYWJsZXMNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCmxvYW5EYXRhIDwtIHJlYWQuY3N2KCdwcm9zcGVyTG9hbkRhdGEuY3N2JykNCmhlYWQobG9hbkRhdGEpDQoNCmBgYA0KIyBGaXJzdCBMb29rDQpJbiB0aGlzIGRhdGEgc2V0IHRoZXJlIGFyZSA4MSBWYXJpYWJsZXMuIFRvIFN0YXJ0IHRoZSBtb3N0IGludGVycmVzdGluZyBmb3IgbWUgc2VlbSB0byBiZQ0KIyBodHRwczovL3d3dy5wcm9zcGVyLmNvbS9Eb3dubG9hZHMvU2VydmljZXMvRG9jdW1lbnRhdGlvbi9Qcm9zcGVyRGF0YUV4cG9ydF9EZXRhaWxzLmh0bWwNCg0KDQpXaGF0Pw0KKiBMb2FuT3JpZ2luYWxBbW91bnQNCiogVGVybTogVGhlIGxlbmd0aCBvZiB0aGUgbG9hbiBleHByZXNzZWQgaW4gbW9udGhzLg0KKiBMb2FuIE9yaWdpbiBEYXRlDQoNCldobz8NCiogaXNCb3Jyb3dlckhvbWVPd25lcg0KKiBPY2N1cGF0aW9uDQoqIENyZWRpdEdyYWRlDQoqIEN1cnJlbnREZWxpbnF1ZW5jaWVzDQoNCldoeT8NCiogTGlzdGluZ0NhdGVnb3J5OiBudW1lcmljIA0KKiogVGhlIGNhdGVnb3J5IG9mIHRoZSBsaXN0aW5nIHRoYXQgdGhlIGJvcnJvd2VyIHNlbGVjdGVkIHdoZW4gcG9zdGluZyB0aGVpciBsaXN0aW5nOiAwIC0gTm90IEF2YWlsYWJsZSwgMSAtIERlYnQgQ29uc29saWRhdGlvbiwgMiAtIEhvbWUgSW1wcm92ZW1lbnQsIDMgLSBCdXNpbmVzcywgNCAtIFBlcnNvbmFsIExvYW4sIDUgLSBTdHVkZW50IFVzZSwgNiAtIEF1dG8sIDctIE90aGVyLCA4IC0gQmFieSZBZG9wdGlvbiwgOSAtIEJvYXQsIDEwIC0gQ29zbWV0aWMgUHJvY2VkdXJlLCAxMSAtIEVuZ2FnZW1lbnQgUmluZywgMTIgLSBHcmVlbiBMb2FucywgMTMgLSBIb3VzZWhvbGQgRXhwZW5zZXMsIDE0IC0gTGFyZ2UgUHVyY2hhc2VzLCAxNSAtIE1lZGljYWwvRGVudGFsLCAxNiAtIE1vdG9yY3ljbGUsIDE3IC0gUlYsIDE4IC0gVGF4ZXMsIDE5IC0gVmFjYXRpb24sIDIwIC0gV2VkZGluZyBMb2Fucw0KDQoqIEJhbmtjYXJkVXRpbGl6YXRpb24NCiogSW5jb21lUmFuZ2UNCiogRW1wbG95bWVudFN0YXR1cw0KDQpIZWxwIGZyb20gRnJpZW5kcz8NCiogSW52ZXN0ZW1lbnRGcm9tRnJpZW5kc0Ftb3VudA0KKiBJbnZlc3RvcnMNCg0KSXMgeW91ciBjcmVkaXQgcmF0ZSBvaz8NCipCb3Jyb3dlckFQUjogVGhlIEJvcnJvd2VyJ3MgQW5udWFsIFBlcmNlbnRhZ2UgUmF0ZSAoQVBSKSBmb3IgdGhlIGxvYW4uDQoqQm9ycm93ZXJSYXRlOiBUaGUgQm9ycm93ZXIncyBpbnRlcmVzdCByYXRlIGZvciB0aGlzIGxvYW4uIFRoZSBpcyB0aGUgcmF0ZSB0aGUgYm9ycm93ZXIgcGF5cyBpZiB0aGUgbG9hbiB3ZXJlIHRvIGNsb3NlIGF0IHRoaXMgcG9pbnQgaW4gdGltZS4gVGhlIHJhdGUgaXMgY29tcHV0ZWQgYXMgdGhlIExlbmRlclJhdGUgKyBHcm91cExlYWRlclJld2FyZFJhdGUgKGlmIGFwcGxpY2FibGUpICsgQmFua0RyYWZ0RmVlQW5udWFsUmF0ZSAoaWYgYXBwbGljYWJsZSkuDQoNCipQcm9zcGVyU2NvcmU6IEEgY3VzdG9tIHJpc2sgc2NvcmUgYnVpbHQgdXNpbmcgaGlzdG9yaWNhbCBQcm9zcGVyIGRhdGEuIFRoZSBzY29yZSByYW5nZXMgZnJvbSAxLTEwLCB3aXRoIDEwIGJlaW5nIHRoZSBiZXN0LCBvciBsb3dlc3QgcmlzayBzY29yZS4gQXBwbGljYWJsZSBmb3IgbG9hbnMgb3JpZ2luYXRlZCBhZnRlciBKdWx5IDIwMDkuDQoNCg0KTG9hbk9yaWdpbmFsQW1vdW50IGFuZCBUZXJtcyBieSBZZWFyDQotIGV4dHJhY3QgdGhlIHllYXIgb2YgdGhlIExvYW5PcmlnaW5EYXRhIGFuZCBzYXZlIGl0IHRvIGEgbmV3IHZhcmlhYmxlIA0KDQoNCiNIb3cgbXVjaCBtb25leSBpcyBuZWVkZWQ/DQpgYGB7cn0NCg0KZ2dwbG90KGFlcyh4ID0gTG9hbk9yaWdpbmFsQW1vdW50KSwgZGF0YSA9IGxvYW5EYXRhKSArIA0KICBnZW9tX2hpc3RvZ3JhbSggZmlsbCA9ICgnI0Y3OTQyMCcpKSANCg0KYGBgDQpgYGB7cn0NCnN1bW1hcnkobG9hbkRhdGEkTG9hbk9yaWdpbmFsQW1vdW50KQ0KYGBgDQojY3JlYXRlIGEgbmV3IERGIHdpdGggaW5mbyBhYm91dCB0aGUgcHJvc3BlciByYXRpbmcgYW5kIHRoZSBhbW91bnQNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmNyZWRpdHNCeUdyYWRlIDwtIGdyb3VwX2J5KGxvYW5EYXRhLCBQcm9zcGVyUmF0aW5nLi5udW1lcmljLikNCmNyZWRpdHNCeUdyYWRlIDwtIHN1bW1hcmlzZShjcmVkaXRzQnlHcmFkZSwNCiAgICBtZWFuX2Ftb3VudCA9IG1lYW4oTG9hbk9yaWdpbmFsQW1vdW50KSwgDQogICAgbWVkaWFuX2Ftb3VudCA9IG1lZGlhbihMb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgIG1pbl9hbW91bnQgPSBtaW4oTG9hbk9yaWdpbmFsQW1vdW50KSwNCiAgICBtYXhfYW1vdW50ID0gbWF4KExvYW5PcmlnaW5hbEFtb3VudCksDQogICAgbiA9IG4oKSkgDQoNCmNyZWRpdHNCeUdyYWRlDQoNCmBgYA0KDQpNb3N0IG9mIGxvYW5zIGFyZSB1bmRlciA1MDAwLC0tLiBJIHN1Z2dlc3QgdGhlIG1vbmV5IGlzIG5lZWRlZCBmb3Igc29uZGVyYXVzZ2FiZW4sIGRpZSBpbSBIYXVzaGFsdHNidWRnZXQga2VpbmVuIFBsYXR6IG1laHIgZ2VmdW5kZW4gaGFiZW4uIE32Z2xpY2hlcndlaXNlIFNjaORkZW4gaW0gSGF1c2hhbHQgb2RlciBTb25kZXJhdXNnYWJlbiBhdWZncnVuZCB2b24gdW52b3JoZXJzZWhiYXJlbiBVbXN05G5kZW4uIFVuZuRsbGUuLi4NCg0KU2VoZW4gd2lyIHVucyBudW4gYW4sIGb8ciB3YXMgZGFzIEdlbGQgYmVu9nRpZ3Qgd3VyZGUuIC0+IHVuc2VyZSBBbm5haG1lIGJlc3TkZ2lnZW4NCmBgYHtyIGxvYW5zIGJ5IGNhdGVnb3J5IH0NCiMgdHJhbnNmb3JtIHRoZSBudW1lcmljIHZhbHVlIG9mIGNhdGVnb3JpZXMgdG8gcmVhZGFibGUgbmFtZXM6DQoNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzAnXSA8LSAnTm90IEF2YWlsYWJsZScNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzEnXSA8LSAnRGVidCBDb25zb2xpZGF0aW9uJw0KbG9hbkRhdGEkQ2F0ZWdvcnlbbG9hbkRhdGEkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMiddIDwtICdIb21lIEltcHJvdmVtZW50Jw0KbG9hbkRhdGEkQ2F0ZWdvcnlbbG9hbkRhdGEkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMyddIDwtICdCdXNpbmVzcycNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzQnXSA8LSAnUGVyc29uYWwgTG9hbicNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzUnXSA8LSAnU3R1ZGVudCBVc2UnDQpsb2FuRGF0YSRDYXRlZ29yeVtsb2FuRGF0YSRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICc2J10gPC0gJ0F1dG8nDQpsb2FuRGF0YSRDYXRlZ29yeVtsb2FuRGF0YSRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICc3J10gPC0gJ090aGVyJw0KbG9hbkRhdGEkQ2F0ZWdvcnlbbG9hbkRhdGEkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnOCddIDwtICdCYWJ5ICYgQWRvcHRpb24nDQpsb2FuRGF0YSRDYXRlZ29yeVtsb2FuRGF0YSRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICc5J10gPC0gJ0JvYXQnDQpsb2FuRGF0YSRDYXRlZ29yeVtsb2FuRGF0YSRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxMCddIDwtICdDb3NtZXRpYyBQcm9jZWR1cmUnDQpsb2FuRGF0YSRDYXRlZ29yeVtsb2FuRGF0YSRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxMSddIDwtICdFbmdhZ2VtZW50IFJpbmcnDQpsb2FuRGF0YSRDYXRlZ29yeVtsb2FuRGF0YSRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxMiddIDwtICdHcmVlbiBMb2FucycNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzEzJ10gPC0gJ0hvdXNlaG9sZCBFeHBlbnNlcycNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzE0J10gPC0gJ0xhcmdlIFB1cmNoYXNlcycNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzE1J10gPC0gJ01lZGljYWwvRGVudGFsJw0KbG9hbkRhdGEkQ2F0ZWdvcnlbbG9hbkRhdGEkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTYnXSA8LSAnTW90b3JjeWNsZScNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzE3J10gPC0gJ1JWJw0KbG9hbkRhdGEkQ2F0ZWdvcnlbbG9hbkRhdGEkTGlzdGluZ0NhdGVnb3J5Li5udW1lcmljLiA9PSAnMTgnXSA8LSAnVGF4ZXMnDQpsb2FuRGF0YSRDYXRlZ29yeVtsb2FuRGF0YSRMaXN0aW5nQ2F0ZWdvcnkuLm51bWVyaWMuID09ICcxOSddIDwtICdWYWNhdGlvbicNCmxvYW5EYXRhJENhdGVnb3J5W2xvYW5EYXRhJExpc3RpbmdDYXRlZ29yeS4ubnVtZXJpYy4gPT0gJzIwJ10gPC0gJ1dlZGRpbmcgTG9hbnMnDQoNCmBgYA0KDQojV0h5Pw0KYGBge3J9DQpyZW9yZGVyX3NpemUgPC0gZnVuY3Rpb24oeCkgew0KICBmYWN0b3IoeCwgbGV2ZWxzID0gbmFtZXMoc29ydCh0YWJsZSh4KSkpKQ0KfQ0KDQpnZ3Bsb3QoZGF0YT0gc3Vic2V0KGxvYW5EYXRhLA0KICAgICAgICAgICAgICAgICAgICBMb2FuT3JpZ2luYWxBbW91bnQgPiAyNTAwICYgTG9hbk9yaWdpbmFsQW1vdW50IDw9IDkwMDAgJiBDYXRlZ29yeSAhPSAnRGVidCBDb25zb2xpZGF0aW9uJyAmIENhdGVnb3J5ICE9ICdOb3QgQXZhaWxhYmxlJyAmIENhdGVnb3J5ICE9ICdPdGhlcicpLCBhZXMocmVvcmRlcl9zaXplKENhdGVnb3J5KSkpICsNCiAgICBnZW9tX2Jhcihjb2xvdXIgPSAiZ3JlZW4iKSsNCiAgICBnZ3RpdGxlKCJBbW91bnQgb2YgbG9hbnMgYnkgY2F0ZWdvcnkiKSArDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSxoanVzdD0xLHZqdXN0PTAuNSkpKw0KICAgIGxhYnMoeD0iIiwgeSA9ICJOdW1iZXIgb2YgbG9hbnMiKSArDQogIGNvb3JkX2ZsaXAoKQ0KDQpgYGANCg0KDQoNCkFzIHRoZSBtb3N0IGNhdGVnb3JpZXMgYXJlIGRlYnQgY29uc29saWRhdGlvbiwgbm90IGF2YWlsYWJlIGFuZCBvdGhlciB3ZSBjYW50IHJlYWxseSB0ZWxsIGlmIGl0IGlzIGxpa2Ugc3VnZ2VzdGVkLiBUaGUgZGF0YSBpcyBub3QgY29tcGxldGUuIEJ1dCBtb3N0bHkgbW9uZXkgaXMgbmVlZGVkIGZvciBob3VzZWhvbGQgZXhwZW5zZXMgb3IgaG9tZSBpbXByb3ZlbWVudC4gDQpXSE8/DQoNCmBgYHtyfQ0Kc3VtbWFyeShsb2FuRGF0YSRPY2N1cGF0aW9uKQ0KYGBgDQpUbyBtYWtlIHRoaXMgZWFzaWVyIHRvIHJlYWQsIHdlIHdhbnQgdG8gY29tYmluZSBncm91cHMgaW50byBhIG5ldyBkYXRhIGZyYW1lLiBTdHVkZW50cywgVHJhZGVzbWFuLCBIZWFsdGgNCmBgYHtyfQ0KDQpsb2FuRGF0YSRHcm91cGVkT2NjdXBhdGlvbiA8LSBmYWN0b3IobG9hbkRhdGEkT2NjdXBhdGlvbikNCmxldmVscyhsb2FuRGF0YSRHcm91cGVkT2NjdXBhdGlvbikgPC0gbGlzdCgNCiAgU3R1ZGVudD1jKCJTdHVkZW50IC0gQ29sbGVnZSBHcmFkdWF0ZSBTdHVkZW50IiwNCiJTdHVkZW50IC0gQ29sbGVnZSBTZW5pb3IiLCANCiJTdHVkZW50IC0gQ29tbXVuaXR5IENvbGxlZ2UiLA0KIlN0dWRlbnQgLSBDb2xsZWdlIEZyZXNobWFuIiwNCiJTdHVkZW50IC0gQ29sbGVnZSBKdW5pb3IiLA0KIlN0dWRlbnQgLSBDb2xsZWdlIFNvcGhvbW9yZSIsDQoiU3R1ZGVudCAtIFRlY2huaWNhbCBTY2hvb2wiKSwgDQpNZWRpY2FsX0hlYWx0aD1jKCJEb2N0b3IiLCAiTnVyc2UncyBBaWRlIiwNCiAgICAgICAgICAgICAgICAgIk51cnNlIChSTikiLA0KICAgICAgICAgICAgICAgICAiTnVyc2UgKExQTikiLA0KICAgICAgICAgICAgICAgICAiRGVudGlzdCIsDQogICAgICAgICAgICAgICAgICJQaGFybWFjaXN0IiwNCiAgICAgICAgICAgICAgICAgIk1lZGljYWwgVGVjaG5pY2lhbiIsDQogICAgICAgICAgICAgICAgICJQc3ljaG9sb2dpc3QiKSwNClNhbGVzPWMoIlNhbGVzIC0gQ29tbWlzc2lvbiIsDQogICAgICAgICJTYWxlcyAtIFJldGFpbCIsDQogICAgICAgICJDYXIgRGVhbGVyIiwNCiAgICAgICAgIlJlYWx0b3IiKSwNClNlcnZpY2U9YygiRm9vZCBTZXJ2aWNlIE1hbmFnZW1lbnQiLA0KICAgICAgICAgICJGb29kIFNlcnZpY2UiLA0KICAgICAgICAgICJQb3N0YWwgU2VydmljZSIsDQogICAgICAgICAgIlNvY2lhbCBXb3JrZXIiLA0KICAgICAgICAgICJUcnVjayBEcml2ZXIiLA0KICAgICAgICAgICJCdXMgRHJpdmVyIiwNCiAgICAgICAgICAiUmV0YWlsIE1hbmFnZW1lbnQiLA0KICAgICAgICAgICJXYWl0ZXIvV2FpdHJlc3MiLA0KICAgICAgICAgICJGbGlnaHQgQXR0ZW5kYW50IiwNCiAgICAgICAgICAiQ2xlcmljYWwiLA0KICAgICAgICAgICJSZWxpZ2lvdXMiLA0KICAgICAgICAgICJDbGVyZ3kiKSwNCkxhYm9yZXI9YygiQ29uc3RydWN0aW9uIiwNCiAgICAgICAgICAiTGFib3JlciIsDQogICAgICAgICAgIlNraWxsZWQgTGFib3IiLA0KICAgICAgICAgICJMYW5kc2NhcGluZyIsDQogICAgICAgICAgIkhvbWVtYWtlciIsDQogICAgICAgICAgIkZpcmVtYW4iLA0KICAgICAgICAgICJFeGVjdXRpdmUiLA0KICAgICAgICAgICJUZWFjaGVyJ3MgQWlkZSIsDQogICAgICAgICAgIkNvbXB1dGVyIFByb2dyYW1tZXIiLA0KICAgICAgICAgICJBZG1pbmlzdHJhdGl2ZSBBc3Npc3RhbnQiLA0KICAgICAgICAgICJQcm9mZXNzaW9uYWwiLA0KICAgICAgICAgICJBY2NvdW50YW50L0NQQSIsDQogICAgICAgICAgIlRyYWRlc21hbiAtIENhcnBlbnRlciIsDQogICAgICAgICAgICAiVHJhZGVzbWFuIC0gTWVjaGFuaWMiLA0KICAgICAgICAgICAgIlRyYWRlc21hbiAtIEVsZWN0cmljaWFuIiwNCiAgICAgICAgICAgICJUcmFkZXNtYW4gLSBQbHVtYmVyIiwNCiAgICAgICAgICAiUGlsb3QgLSBQcml2YXRlL0NvbW1lcmNpYWwiKSwNCkhpZ2hlckVkSm9icz1jKCJBcmNoaXRlY3QiLA0KICAgICAgICAgICAgICAgIkJpb2xvZ2lzdCIsDQogICAgICAgICAgICAgICAiRW5naW5lZXIgLSBFbGVjdHJpY2FsIiwNCiAgICAgICAgICAgICAgICJFbmdpbmVlciAtIE1lY2hhbmljYWwiLA0KICAgICAgICAgICAgICAgIkVuZ2luZWVyIC0gQ2hlbWljYWwiLA0KICAgICAgICAgICAgICAgIkp1ZGdlIiwgIlRlYWNoZXIiLA0KICAgICAgICAgICAgICAgIlNjaWVudGlzdCIsDQogICAgICAgICAgICAgICAiUHJvZmVzc29yIiwNCiAgICAgICAgICAgICAgICJBdHRvcm5leSIsICJBbmFseXN0IiwgIkFjY291bnRhbnQvQ1BBIg0KICAgICAgICAgICAgICAgKSwNCkNpdmlsU2VydmljZT1jKCJDaXZpbCBTZXJ2aWNlIiwNCiAgICAgICAgICAgICAgICJNaWxpdGFyeSBPZmZpY2VyIiwNCiAgICAgICAgICAgICAgICJQb2xpY2UgT2ZmaWNlci9Db3JyZWN0aW9uIE9mZmljZXIiLA0KICAgICAgICAgICAgICAgIk1pbGl0YXJ5IEVubGlzdGVkIiksDQpPdGhlcj1jKCJPdGhlciIsICIiKQ0KKQ0KDQoNCmdncGxvdChkYXRhPXN1YnNldChsb2FuRGF0YSwgR3JvdXBlZE9jY3VwYXRpb24gIT0gJ090aGVyJyAmICFpcy5uYShHcm91cGVkT2NjdXBhdGlvbikpLCB4PUdyb3VwZWRPY2N1cGF0aW9uLCBhZXMocmVvcmRlcl9zaXplKEdyb3VwZWRPY2N1cGF0aW9uKSkpICsNCiAgICBnZW9tX2JhcigpKw0KICAgIGdndGl0bGUoIkJvcnJvd2VycyBieSBPY2N1cGF0aW9uIikrDQogICAgbGFicyh4PSIiLCB5ID0gIk51bWJlciBvZiBsb2FucyIpKw0KICAgICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSx2anVzdD0wLjUpKQ0KDQpgYGANCg0KSW4gdGhlIG5leHQgc3RlcCBJIHdhbnQgdG8gcHJvb2YgdGhlIHBlb3BsZSB3aG8gYXJlbiB0IGhvdXNlIG93bmVycyBuZWVkIG1vcmUgb2Z0ZW4gc21hbGwgbG9hbnMgZm9yIFZhY2F0aW9uLCBIb21lSW1wcm92ZW1lbnQgb3IgSG91c2Vob2xkIEV4cGVuc2VzLg0KDQpgYGB7cn0NCnN1bW1hcnkobG9hbkRhdGEkSXNCb3Jyb3dlckhvbWVvd25lcikNCmBgYA0KDQpob3VzZSBvd25lciBuZWVkIGxlc3MgY3JlZGl0cw0KTkVYdDogbGltaXQgdG8gc21hbGxlciBhbW91bnQgb2YgbG9hbiBhbmQgY2hlY2sgaWYgbG9hbiB0YWtlciBpcyBhIGhvdXNlIG93bmVyDQoNCg0KDQoNCg0KDQpgYGB7cn0NCiAgcXBsb3QoeD1Jc0JvcnJvd2VySG9tZW93bmVyLCB5PUxvYW5PcmlnaW5hbEFtb3VudCwNCiAgICAgICAgZGF0YT1sb2FuRGF0YSwgZ2VvbSA9ICdib3hwbG90JykgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxMDAwLCAyMDAwMCkpDQpgYGANCg0KDQojcmVsYXRpb24gYmV0d2VlbiBwcm9zcGVyIHJhdGUgYW5kIGhvdXNlT3duZXINCmBgYHtyfQ0KDQpxcGxvdCh4PUlzQm9ycm93ZXJIb21lb3duZXIsIHk9UHJvc3BlclNjb3JlLA0KICAgICAgICBkYXRhPWxvYW5EYXRhLCBnZW9tID0gJ2JveHBsb3QnKSArDQogIHNjYWxlX3lfY29udGludW91cygpDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChhZXMoeCA9IElzQm9ycm93ZXJIb21lb3duZXIsIHkgPSBMb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgICAgZGF0YSA9IGxvYW5EYXRhKSArDQogICAgICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBtZWFuLCBnZW9tPSAncG9pbnQnLCBzaGFwZSA9IDQpDQoNCmdncGxvdChhZXMoeD1Hcm91cGVkT2NjdXBhdGlvbiwgeT1Mb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgICAgZGF0YSA9IGxvYW5EYXRhKSArDQogICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSx2anVzdD0wLjUpKSsNCiAgICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yPSBJc0JvcnJvd2VySG9tZW93bmVyLCBhbHBoYT0xLzIwKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChhZXMoeD1Hcm91cGVkT2NjdXBhdGlvbiwgeT1Mb2FuT3JpZ2luYWxBbW91bnQpLA0KICAgICAgZGF0YSA9IGxvYW5EYXRhKSArDQogICAgICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MC41LHZqdXN0PTAuNSkpKw0KICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9IElzQm9ycm93ZXJIb21lb3duZXIpKSsNCnNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygwLCA1MDAwKSkNCmBgYA0KI2ZvciBtb25leSBsZXNzIHRoYW4gNTAwMCB0aGUgbWFqb3JpdHkgb2YgdGhlIGJvcnJvd2VycyBhcmVuIHQgaG91c2Vvd25lcnMuIE1heWJlIHRoZSBncm91cGluZyBpcyBub3QgY29ycmVjdC4gaWUgaW4gZ3JvdXAgbWVkaWFjbCBuZCBoZWxwIHRoZXJlIGFyZSBkb2N0b3JzLCBudXJzZXMgZXQuLi4NCg0KDQojY2hlY2sgaWYgaG9tZW93bmVyIGhhdmUgYSBiZXR0ZXIgUHJvc3BlcnNjb3JlDQojUHJvc3BlciBBIGN1c3RvbSByaXNrIHNjb3JlIGJ1aWx0IHVzaW5nIGhpc3RvcmljYWwgUHJvc3BlciBkYXRhLiBUaGUgc2NvcmUgcmFuZ2VzIGZyb20gMS0xMCwgd2l0aCAxMCBiZWluZyB0aGUgYmVzdCwgb3IgbG93ZXN0IHJpc2sgc2NvcmUuDQoNCiNCb3Jyb3dlclJhdGU6IFRoZSBpcyB0aGUgcmF0ZSB0aGUgYm9ycm93ZXIgcGF5cyBpZiB0aGUgbG9hbiB3ZXJlIHRvIGNsb3NlIGF0IHRoaXMgcG9pbnQgaW4gdGltZS4gVGhlIHJhdGUgaXMgY29tcHV0ZWQgYXMgdGhlIExlbmRlclJhdGUgKyBHcm91cExlYWRlclJld2FyZFJhdGUgKGlmIGFwcGxpY2FibGUpICsgQmFua0RyYWZ0RmVlQW5udWFsUmF0ZSAoaWYgYXBwbGljYWJsZSkuDQoNCkJvcnJvd2VyIFJhdGU6IFRoZSBpcyB0aGUgcmF0ZSB0aGUgYm9ycm93ZXIgcGF5cyBpZiB0aGUgbG9hbiB3ZXJlIHRvIGNsb3NlIGF0IHRoaXMgcG9pbnQgaW4gdGltZS4gVGhlIHJhdGUgaXMgY29tcHV0ZWQgYXMgdGhlIExlbmRlclJhdGUgKyBHcm91cExlYWRlclJld2FyZFJhdGUgKGlmIGFwcGxpY2FibGUpICsgQmFua0RyYWZ0RmVlQW5udWFsUmF0ZSAoaWYgYXBwbGljYWJsZSkuDQoNClByb3NwZXJTY29yZTogQSBjdXN0b20gcmlzayBzY29yZSBidWlsdCB1c2luZyBoaXN0b3JpY2FsIFByb3NwZXIgZGF0YS4gVGhlIHNjb3JlIHJhbmdlcyBmcm9tIDEtMTAsIHdpdGggMTAgYmVpbmcgdGhlIGJlc3QsIG9yIGxvd2VzdCByaXNrIHNjb3JlLg0KDQpUb3RhbCBDcmVkaXQgTGluZXMgcGFzdCA3IHllYXJzOg0KDQpgYGB7cn0NCnN1bW1hcnkobG9hbkRhdGEkTW9udGhseUxvYW5QYXltZW50KQ0KI3Byb3NwZXIgc2NvcmUgLT4gaG91c2Ugb3duZXJzIGhhdmUgYSBiZXR0ZXIgb25lDQpnZ3Bsb3QoYWVzKHg9Q3VycmVudERlbGlucXVlbmNpZXMsIHk9Li5jb3VudC4uL3N1bSguLmNvdW50Li4pKSwgZGF0YSA9IHN1YnNldChsb2FuRGF0YSwgIWlzLm5hKElzQm9ycm93ZXJIb21lb3duZXIpKSkgKw0KICBnZW9tX2ZyZXFwb2x5KGFlcyhjb2xvciA9IElzQm9ycm93ZXJIb21lb3duZXIpKSArIA0KICB4bGFiKCdQcm9zcGVyIFJhdGluZycpICsgDQogIHlsYWIoJ1BlcmNlbnRhZ2Ugb2YgQm9ycm93ZXJzIHdpdGggdGhhdCBQcm9zcGVyIFJhdGluZycpDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChhZXMoeD1Cb3Jyb3dlclJhdGUsIHk9UHJvc3BlclNjb3JlKSwgZGF0YSA9IGxvYW5EYXRhKSsNCiAgZ2VvbV9saW5lKCkrDQogIGdlb21fc21vb3RoKCkNCmBgYA0Kbm8gc3VycHJpc2VzIGhlcmUsIHRoZSBiZXR0ZXIgdGhlIFByb3NwZXIgU2NvcmUsIHRoZSBiZXR0ZXIgdGhlIEJvcnJvd2VyIFJhdGUgLT4NCkhvY2tleSBTdGljayA/Pw0KDQoNCiNieUluY29tZVJBbmdlLi4NCg0KDQoNCg0KV2hlbiB3YXMgdGhlIG1vbmV5IG5lZWRlZD8NCg0KYGBge3IgTG9hbnMgb3ZlciB0aW1lfQ0KbG9hbkRhdGEkTG9hbk9yaWdpbmF0aW9uWWVhciA8LSBmb3JtYXQoYXMuRGF0ZShsb2FuRGF0YSRMb2FuT3JpZ2luYXRpb25EYXRlLCBmb3JtYXQ9IiVZLSVtLSVkIiksIiVZIikNCg0KZ2dwbG90KGFlcyh4ID0gTG9hbk9yaWdpbmF0aW9uWWVhciwgeSA9IExvYW5PcmlnaW5hbEFtb3VudCwgZmlsbD1UZXJtLCBncm91cD1Mb2FuT3JpZ2luYXRpb25ZZWFyKSwgZGF0YSA9bG9hbkRhdGEpICsNCiAgICBnZW9tX2JhcihhZXMoZ3JvdXAgPSBMb2FuT3JpZ2luYXRpb25ZZWFyKSwgcG9zaXRpb249J2RvZGdlJywgc3RhdD0naWRlbnRpdHknKSsNCiAgICBnZ3RpdGxlKCJMb2FucyBieSBZZWFyIikgKw0KICAgIGxhYnMoeD0iTG9hbiBvcmlnaW5hdGlvbiB5ZWFyIiwgeSA9ICJMb2FuIG9yaWdpbmFsIGFtb3VudCIpDQpgYGANCkluY29tZVJhbmdlOg0KDQpJbmNvbWU6IFRoZSBpbmNvbWUgcmFuZ2Ugb2YgdGhlIGJvcnJvd2VyIGF0IHRoZSB0aW1lIHRoZSBsaXNpdG5nIHdhcyBjcmVhdGVkIGlzIG9uZSBvZiB0aGUgZm9sbG93aW5nIHZhbHVlczoNCjAgLSBOb3QgZGlzcGxheWVkDQoxIC0gJDAgb3IgdW5hYmxlIHRvIHZlcmlmeQ0KMiAtICQxLTI0LDk5OQ0KMyAtICQyNSwwMDAtNDksOTk5DQo0IC0gJDUwLDAwMC03NCw5OTkNCjUgLSAkNzUsMDAwLTk5LDk5OQ0KNiAtICQxMDAsMDAwKw0KNyAtIE5vdCBFbXBsb3llZA0KDQpJbmNvbWUgdmVyaWZ5YWxibGU6DQoNCkN1cnJlbnREZWxpbnF1ZW5pZXM6IE51bWJlciBvZiBjdXJyZW50IGRlbGlucXVlbmNpZXMgYXQgdGhlIHRpbWUgdGhlIGxpc3Rpbmcgd2FzIGNyZWF0ZWQuDQoNCg0KSXMgdGhlcmUgYSBjb25uZWN0aW9uIGJldHdlZW4gaW5jb21lIG9mIGJvcnJvd2VycyBhbmQgDQoNCmBgYHtyfQ0KZ2dwbG90KGFlcyh4PVByb3NwZXJSYXRpbmcuLm51bWVyaWMuLCB5PS4uY291bnQuLi9zdW0oLi5jb3VudC4uKSksIGRhdGEgPSBzdWJzZXQobG9hbkRhdGEsICFpcy5uYShJbmNvbWVWZXJpZmlhYmxlKSkpICsNCiAgZ2VvbV9mcmVxcG9seShhZXMoY29sb3IgPSBJbmNvbWVWZXJpZmlhYmxlKSkgKyANCiAgeGxhYignUHJvc3BlciBSYXRpbmcnKSArIA0KICB5bGFiKCdQZXJjZW50YWdlIG9mIEJvcnJvd2VycyB3aXRoIHRoYXQgUHJvc3BlciBSYXRpbmcnKQ0KDQpgYGANCg0KRW1wbG95bWVudFN0YXR1czogRW1wbG95bWVudCBTdGF0dXMgRHVyYXRpb24gSW4gTW9udGhzLg0KDQoNCmBgYHtyfQ0KDQpzdW1tYXJ5KGxvYW5EYXRhJEVtcGxveW1lbnRTdGF0dXNEdXJhdGlvbikNCmxvYW5EYXRhJGJ1Y2tldF9FbXBsb3ltZW50U3RhdHVzRHVyYXRpb24gPC0gY3V0KGxvYW5EYXRhJEVtcGxveW1lbnRTdGF0dXNEdXJhdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKDAsIDEyLCAyNCwgMzYsIDQ4LCA1MCwgNjIsIDc0LCA4NiwgOTgsIDExMCwgMTIyLCAxMzQsIDE0NiwgNzU1KSkNCg0KDQpnZ3Bsb3QoYWVzKHggPSBDcmVkaXRHcmFkZSwgeT1FbXBsb3ltZW50U3RhdHVzRHVyYXRpb24pLA0KICAgICAgIGRhdGEgPSBsb2FuRGF0YSkgKw0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IA0KICAgICAgICAgICAgICAgICAgRW1wbG95bWVudFN0YXR1cykpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KDQpnZ3Bsb3QoYWVzKHg9Q3JlZGl0R3JhZGUsIHk9RW1wbG95bWVudFN0YXR1c0R1cmF0aW9uKSwgZGF0YSA9IGxvYW5EYXRhKSArIA0KICBnZW9tX3BvaW50KGFlcyhjb2xvcj1FbXBsb3ltZW50U3RhdHVzKSkNCg0KI2dncGxvdChhZXMoeD1Mb2FuT3JpZ2luYXRpb25EYXRlLCB5PUxvYW5PcmlnaW5hbEFtb3VudCksIGRhdGEgPSBsb2FuRGF0YSkgKyANCiMgIGdlb21fcG9pbnQoYWVzKGNvbG9yPUluY29tZVJhbmdlKSkNCg0KIyAgZGV0YWNoKCJwYWNrYWdlOnBseXIiLCB1bmxvYWQ9VFJVRSkNCg0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGFlcyh4PUNyZWRpdEdyYWRlLCB5PWJ1Y2tldF9FbXBsb3ltZW50U3RhdHVzRHVyYXRpb24pLCBkYXRhID0gbG9hbkRhdGEpICsgDQogIGdlb21fcG9pbnQoYWVzKGNvbG9yPUVtcGxveW1lbnRTdGF0dXMpKQ0KDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoYWVzKHg9VGVybSwgeT1Mb2FuT3JpZ2luYWxBbW91bnQpLCBkYXRhID0gbG9hbkRhdGEpICsNCiAgICBmYWNldF93cmFwKH5Hcm91cGVkT2NjdXBhdGlvbikgKw0KICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gQ2F0ZWdvcnkpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygxMDAwLCA1MDAwKSkNCmBgYA0KYGBge3J9DQpsb2FuRGF0YSRsb2FuX2luY29tZV9yYXRpbyA9IGxvYW5EYXRhJExvYW5PcmlnaW5hbEFtb3VudC9sb2FuRGF0YSRTdGF0ZWRNb250aGx5SW5jb21lDQpnZ3Bsb3QoZGF0YSA9IGxvYW5EYXRhLCBhZXMoeCA9IGxvYW5faW5jb21lX3JhdGlvKSkgKyAgICAgICAgICAgICAgICANCiAgICAgICAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siLCBmaWxsID0gJyMwMDdFRTUnLCBiaW53aWR0aCA9IDAuMDIpICsNCiAgICAgICAgeGxpbSgwLCBxdWFudGlsZShsb2FuRGF0YSRsb2FuX2luY29tZV9yYXRpbywgcHJvYiA9IDAuNTcsIG5hLnJtPVRSVUUpKSArDQogICAgICAgIGdndGl0bGUoIkRlYnQgVG8gSW5jb21lIFJhdGlvIikgKw0KICAgICAgICB4bGFiKCJEZWJ0IHRvIEluY29tZSBSYXRpbyIpICsNCiAgICAgICAgeWxhYigiQ291bnQiKQ0Kc3VtbWFyeShsb2FuRGF0YSRsb2FuX2luY29tZV9yYXRpbykNCg0KYGBgDQoNCg0KTG9hbk9yaWdpbmFsQW1vdW50OiBBbW91bnQgbmVlZGVkDQoNCkJvcnJvd2VyIFJhdGU6IFRoZSBib3Jyb3dlciByYXRlIGZvciB0aGlzIGxvYW4uDQoNCkxlbmRlciBZaWVsZDogVGhlIGludGVyZXN0IHJhdGUgdGhlIGxlbmRlciByZWNlaXZlcyBvbiB0aGUgbG9hbi4NCg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpnZ3Bsb3QobG9hbkRhdGEsIGFlcyh4ID0gTGVuZGVyWWllbGQsIHkgPSBCb3Jyb3dlclJhdGUpKSArDQogIGdlb21fcG9pbnQoYWxwaGE9MS8yMCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoMCwgcXVhbnRpbGUobG9hbkRhdGEkTGVuZGVyWWllbGQsIDAuOTkpKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoIA0KICAgICAgICAgICAgICAgICAgICAgbGltaXRzPWMoMCAsIHF1YW50aWxlKGxvYW5EYXRhJEJvcnJvd2VyUmF0ZSwgMC45OSkpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChsb2FuRGF0YSwgYWVzKHggPSBCb3Jyb3dlclJhdGUsIHkgPSBMZW5kZXJZaWVsZCwNCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gSXNCb3Jyb3dlckhvbWVvd25lciwgc2l6ZT1jb3VudCkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IHN1YnNldChsb2FuRGF0YSwgQ2F0ZWdvcnkgJWluJSBjKA0KICAgIA0KJ0RlYnQgQ29uc29saWRhdGlvbicsICdIb21lIEltcHJvdmVtZW50Jw0KLCdCdXNpbmVzcycNCiwnUGVyc29uYWwgTG9hbicNCiwnU3R1ZGVudCBVc2UnDQosJ0F1dG8nDQosJ090aGVyJw0KLCdCYWJ5ICYgQWRvcHRpb24nDQosJ0JvYXQnDQosJ0Nvc21ldGljIFByb2NlZHVyZScNCiwnRW5nYWdlbWVudCBSaW5nJw0KLCdHcmVlbiBMb2FucycNCiwnSG91c2Vob2xkIEV4cGVuc2VzJw0KLCdMYXJnZSBQdXJjaGFzZXMnDQosJ01lZGljYWwvRGVudGFsJw0KLCdNb3RvcmN5Y2xlJw0KLCdSVicNCiwnVGF4ZXMnDQosJ1ZhY2F0aW9uJw0KLCdXZWRkaW5nIExvYW5zJw0KICApKSwgDQogICAgICAgICAgICAgYWVzKGxhYmVsID0gTG9hbnMpLA0KICAgICAgICAgICAgIGNvbG9yPSIjMDI2NmM4IikgKw0KICBnZW9tX3RleHQoZGF0YSA9IHN1YnNldChsb2FuRGF0YSwgQ2F0ZWdvcnkgJWluJSBjKA0KICAgIA0KICAgICdEZWJ0IENvbnNvbGlkYXRpb24nLCAnSG9tZSBJbXByb3ZlbWVudCcNCiwnQnVzaW5lc3MnDQosJ1BlcnNvbmFsIExvYW4nDQosJ1N0dWRlbnQgVXNlJw0KLCdBdXRvJw0KLCdPdGhlcicNCiwnQmFieSAmIEFkb3B0aW9uJw0KLCdCb2F0Jw0KLCdDb3NtZXRpYyBQcm9jZWR1cmUnDQosJ0VuZ2FnZW1lbnQgUmluZycNCiwnR3JlZW4gTG9hbnMnDQosJ0hvdXNlaG9sZCBFeHBlbnNlcycNCiwnTGFyZ2UgUHVyY2hhc2VzJw0KLCdNZWRpY2FsL0RlbnRhbCcNCiwnTW90b3JjeWNsZScNCiwnUlYnDQosJ1RheGVzJw0KLCdWYWNhdGlvbicNCiwnV2VkZGluZyBMb2FucycpKSwNCiAgICAgICAgICAgIGFlcyhsYWJlbCA9IExvYW5zKSwNCiAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwNCiAgICAgICAgICAgIHNpemUgPSAzLA0KICAgICAgICAgICAgdmp1c3QgPSAtMC41KSArDQogICNnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGxpbmV0eXBlPTIsIGNvbG9yPSJvcmFuZ2UiKSArDQogIHlsYWIoIkF2ZXJhZ2UgU2NpZW5jZSBTY29yZSIpICsgDQogIHhsYWIoIkF2ZXJhZ2UgUmVhZGluZyBTY29yZSIpICsgDQogIGdndGl0bGUoIkF2ZXJhZ2UgUmVhZGluZyBhbmQgU2NpZW5jZSBTY29yZXMgcGVyIGNvdW50cnksIGJ5IGdlbmRlciIpDQoNCmBgYA0KDQojIHdoYXQgaGFwcGVuZW5kIGluIDIwMTE/DQoNCg0KbkV4dDogDQoNCg0KI1Byb2JsZW1zDQotQ29udmVydCBPcmlnaW4gRGF0ZSB0byBvbmx5IHRoZSB5ZWFyLi4NCi1NYXAgUmVhZGFibGUgdmFsdWVzIHRvIENhdGVnb3J5IG51bWVyaWMuLg0KLSBKb2IgZHVyYXRpb24gaW4gbW9udGhzDQoNCg0K